package xml;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import logic.Logic;

import org.w3c.dom.Attr;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

public class DomXmlWriter {

	private Document createDocument() throws ParserConfigurationException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document doc = db.newDocument();
		return doc;
	}

	private Transformer setUpTransformer()
			throws TransformerConfigurationException {
		TransformerFactory transfac = TransformerFactory.newInstance();
		Transformer trans = transfac.newTransformer();
		trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
		trans.setOutputProperty(OutputKeys.INDENT, "yes");
		trans.setOutputProperty(OutputKeys.METHOD, "xml");
		return trans;
	}

	private Element parseObject(Object object, Document doc) {
		Element root = null;
		if (object instanceof ArrayList) {
			root = parseArray(object, doc);
		} else {
			root = parseSimpleObject(object, doc);
		}
		return root;
	}

	private Element parseSimpleObject(Object object, Document doc) {
		Element root = null;
		Field currentField = null;
		try {
			String rootName = object.getClass().getSimpleName();
			root = doc.createElement(rootName);

			// create a comment and put it in the root element
			Comment comment = doc.createComment("Object by my parser:)");
			root.appendChild(comment);

			Field[] fields = object.getClass().getDeclaredFields();
			AccessibleObject.setAccessible(fields, true);

			for (Field f : fields) {
				currentField = f;
				Object value = f.get(object);
				if (Logic.isSimpleType(value)) {
					Element child = doc.createElement(f.getName());
					Text text = doc.createTextNode(value.toString());
					root.appendChild(child);
					child.appendChild(text);
				}
			}
		} catch (IllegalArgumentException e) {
			System.out.println(currentField);
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			System.out.println(currentField);
			e.printStackTrace();
		}
		return root;
	}

	private Element parseArray(Object object, Document doc) {
		Element root = null;
	
		ArrayList<?> l = (ArrayList<?>) object;
		String rootName;
		
		if (l.size() > 0) {
			rootName = l.getClass().getSimpleName();
			Attr attr = doc.createAttribute("type");
			attr.setValue(l.get(0).getClass().getName());			
			root = doc.createElement(rootName);
			root.setAttributeNode(attr);
			// create a comment and put it in the element
			Comment comment = doc.createComment("ArrayList by my parser:)");
			root.appendChild(comment);

			for (Object o : l) {
				root.appendChild(parseObject(o, doc));
			}
		} else {
			// TODO say that we have empty list
		}
		return root;
	}

	public String showObject(Object object) {
		String xmlString = null;
		try {
			// Creating an empty XML Document
			Document doc = createDocument();
			// Making a tree from the object
			doc.appendChild(parseObject(object, doc));

			// Output the XML

			// set up a transformer
			Transformer trans = null;
			trans = setUpTransformer();

			// create string from xml tree
			StringWriter sw = new StringWriter();
			StreamResult result = new StreamResult(sw);
			DOMSource source = new DOMSource(doc);
			trans.transform(source, result);

			xmlString = sw.toString();

		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
		return xmlString;
	}

	public void saveToFile(Object object, String fileName) {
		PrintWriter pw = null;
		boolean append = false;
		try {
			pw = new PrintWriter(new FileWriter(fileName, append), true);
			pw.write(showObject(object));
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			Logic.silentClose(pw);
		}
	}

}
